// Copyright 2019 Istio Authors
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package main

import (
	"path/filepath"
	"strings"

	"google.golang.org/protobuf/compiler/protogen"
	"google.golang.org/protobuf/types/descriptorpb"
)

func main() {
	protogen.Options{}.Run(func(gen *protogen.Plugin) error {
		for _, f := range gen.Files {
			if !f.Generate {
				continue
			}
			generateFile(gen, f)
		}
		return nil
	})
}

func generateFile(gen *protogen.Plugin, file *protogen.File) {
	ourVersion := filepath.Base(filepath.Dir(file.Desc.Path()))
	var versions []string
	for _, msg := range file.Messages {
		for _, line := range strings.Split(msg.Comments.Leading.String(), "\n") {
			// Looking for something like '// +cue-gen:Simple:versions:v1,v1alpha'
			if strings.HasPrefix(line, "// +cue-gen:") {
				items := strings.Split(line, ":")
				if len(items) != 4 {
					continue
				}
				if items[2] == "versions" {
					for _, v := range strings.Split(items[3], ",") {
						if v != ourVersion {
							versions = append(versions, v)
						}
					}
				}
			}
		}
	}
	if len(versions) == 0 {
		return
	}
	base := filepath.Dir(filepath.Dir(file.Desc.Path()))
	fnamePrefix := filepath.Base(file.GeneratedFilenamePrefix)
	for _, aliasVersion := range versions {
		filename := filepath.Join(base, aliasVersion, fnamePrefix+"_alias.gen.go")
		p := gen.NewGeneratedFile(filename, file.GoImportPath)

		p.P("// Code generated by protoc-gen-alias. DO NOT EDIT.")
		p.P("package ", aliasVersion)
		p.P(`import `, file.GoImportPath)
		var processMessages func([]*protogen.Message)
		var processEnums func([]*protogen.Enum)
		var processOneofs func([]*protogen.Oneof)

		processEnums = func(enums []*protogen.Enum) {
			for _, e := range enums {
				typeName := e.GoIdent.GoName
				p.P(e.Comments.Leading, `type `, typeName, `= `, file.GoPackageName, ".", e.GoIdent)
				for _, v := range e.Values {
					p.P(v.Comments.Leading, `const `, v.GoIdent, " ", typeName, `= `, file.GoPackageName, ".", v.GoIdent)
				}
			}
		}
		processOneofs = func(oneofs []*protogen.Oneof) {
			for _, e := range oneofs {
				for _, f := range e.Fields {
					p.P(f.Comments.Leading, `type `, f.GoIdent, `= `, file.GoPackageName, ".", f.GoIdent)
				}
			}
		}
		processMessages = func(messages []*protogen.Message) {
			for _, message := range messages {
				// skip maps in protos.
				if message.Desc.Options().(*descriptorpb.MessageOptions).GetMapEntry() {
					continue
				}
				typeName := message.GoIdent.GoName
				p.P(message.Comments.Leading, `type `, typeName, "= ", file.GoPackageName, ".", message.GoIdent)
				processMessages(message.Messages)
				processEnums(message.Enums)
				processOneofs(message.Oneofs)
			}
		}
		processMessages(file.Messages)
		processEnums(file.Enums)
	}
}
